

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>

#include "cJSON.h"
#include "wifi_data.h"
#include "conf.h"

#include "ghttp.h"
#include "ghttp_constants.h"

static pthread_mutex_t wifi_data_metex_ap = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t wifi_data_metex_sta = PTHREAD_MUTEX_INITIALIZER;

static cJSON *ap_mac_head = NULL;
static cJSON *sta_mac_head = NULL;

static cJSON *ap_mac_list = NULL;
static cJSON *sta_mac_list = NULL;

static __attribute__((unused)) const char *WIFI_AP_INFO_KEY_SSID = "ssid";
static __attribute__((unused)) const char *WIFI_AP_INFO_KEY_SIGNAL = "signal";
static __attribute__((unused)) const char *WIFI_AP_INFO_KEY_CHANNEL = "channel";

static __attribute__((unused)) const char *WIFI_STA_INFO_KEY_SRCMAC = "src_mac";
static __attribute__((unused)) const char *WIFI_STA_INFO_KEY_DSTMAC = "dst_mac";
static __attribute__((unused)) const char *WIFI_STA_INFO_KEY_RSSI = "rssi";

static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_TOTAL = "total";
static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_DATA = "data";
static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_MODEL = "model";
static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_MAC = "mac";
static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_SSID = "ssid";
static __attribute__((unused)) const char *WIFI_TOTAL_DATA_KEY_DISTANCE = "distance";

static __inline void wifi_data_ap_lock(void)
{
    pthread_mutex_lock(&wifi_data_metex_ap);
}

static __inline void wifi_data_ap_unlock(void)
{
    pthread_mutex_unlock(&wifi_data_metex_ap);
}

static __inline void wifi_data_sta_lock(void)
{
    pthread_mutex_lock(&wifi_data_metex_sta);
}

static __inline void wifi_data_sta_unlock(void)
{
    pthread_mutex_unlock(&wifi_data_metex_sta);
}

static __inline bool json_array_member_exist(const char *member, cJSON *array)
{
    int count = cJSON_GetArraySize(array);
    bool flag = false;

    int i = 0;
    for (; i < count; ++i)
    {
        if (strcasecmp(member, cJSON_GetArrayItem(array, i)->valuestring) == 0)
        {
            flag = true;
            break;
        }
    }

    return flag;
}

static __inline void wifi_data_add_ap_to_json(WIFI_AP_INFO *ap_info)
{
    if (!json_array_member_exist(ap_info->bssid, ap_mac_head))
    {
        cJSON_AddItemToArray(ap_mac_head, cJSON_CreateString(ap_info->bssid));

        cJSON *sub_root = cJSON_CreateObject();

        cJSON_AddStringToObject(sub_root, WIFI_AP_INFO_KEY_SSID, ap_info->ssid);
        cJSON_AddNumberToObject(sub_root, WIFI_AP_INFO_KEY_CHANNEL, ap_info->channel);
        cJSON_AddNumberToObject(sub_root, WIFI_AP_INFO_KEY_SIGNAL, ap_info->siganl);

        cJSON_AddItemToObject(ap_mac_list, ap_info->bssid, sub_root);
    }
}

static __inline void wifi_data_add_sta_to_json(wifi_data_t *sta_info)
{
    if (!json_array_member_exist(sta_info->src_mac, sta_mac_head))
    {
        cJSON_AddItemToArray(sta_mac_head, cJSON_CreateString(sta_info->src_mac));
        cJSON *sub_root = cJSON_CreateObject();

        cJSON_AddStringToObject(sub_root, WIFI_STA_INFO_KEY_DSTMAC, sta_info->dst_mac);
        cJSON_AddNumberToObject(sub_root, WIFI_STA_INFO_KEY_RSSI, sta_info->rssi);

        cJSON_AddItemToObject(sta_mac_list, sta_info->src_mac, sub_root);
    }
}

static __inline void wifi_data_sta_info(wifi_data_t *wdata, cJSON *sta_mac_list_sub)
{

    memset(wdata, 0, sizeof(wifi_data_t));

    char *dst_mac = cJSON_GetObjectItem(sta_mac_list_sub, WIFI_STA_INFO_KEY_DSTMAC)->valuestring;
    memcpy(wdata->dst_mac, dst_mac, strlen(dst_mac));
    wdata->rssi = cJSON_GetObjectItem(sta_mac_list_sub, WIFI_STA_INFO_KEY_RSSI)->valueint;
}

static __inline void wifi_data_ap_info(report_data_t *ap_info, cJSON *ap_mac_list_sub)
{
    //memset(ap_info, 0, sizeof(report_data_t));

    char *ssid = cJSON_GetObjectItem(ap_mac_list_sub, WIFI_AP_INFO_KEY_SSID)->valuestring;
    memcpy(ap_info->ap_ssid, ssid, strlen(ssid));
    // ap_info->channel = cJSON_GetObjectItem(ap_mac_list_sub, WIFI_AP_INFO_KEY_CHANNEL)->valueint;
    //  ap_info->siganl = cJSON_GetObjectItem(ap_mac_list_sub, WIFI_AP_INFO_KEY_SIGNAL)->valueint;
}

static char *boundary = "----WebKitFormBoundaryfo2G5TACF8d14TBK";
static __inline int http_post(const char *uri, const char *params, int timeout, char *result, int resultLen)
{
    if (uri == NULL || params == NULL)
        return -1;

    ghttp_request *request = NULL;
    ghttp_status status;

    request = ghttp_request_new();

    if (ghttp_set_uri(request, (char *)uri) == -1)
        return -1;
    if (ghttp_set_type(request, ghttp_type_post) == -1)
        return -1;

    char post_header[1024];
    snprintf(post_header, 1024, "multipart/form-data; boundary=%s", boundary);
    ghttp_set_header(request, http_hdr_Content_Type, post_header);

    char timeoutStr[10];
    snprintf(timeoutStr, 10, "%d", timeout);

    ghttp_set_header(request, http_hdr_Timeout, timeoutStr);

    ghttp_set_body(request, (char *)params, strlen(params));
    ghttp_prepare(request);
    if ((status = ghttp_process(request)) == ghttp_error)
        return -1;

    char *tmp = ghttp_get_body(request);
    resultLen = ghttp_get_body_len(request);
    memcpy(result, tmp, resultLen);

    ghttp_clean(request);
    ghttp_request_destroy(request);
    return resultLen;
}

static double rssi_to_distance(double txPower, double rssi)
{
    double refDistance = 1.0;
    double pathLoss = 3.2;

    double c1 = txPower;
    double c2 = pathLoss;

    double fm = (c1 - rssi) / (10 * c2);
    double fp = pow(10, fm);
    double ret = refDistance * fp / 100;

    return ret;
}

static __inline void report_data_to_json_array(cJSON *json_array, const report_data_t *rdata)
{
    cJSON *json_data = cJSON_CreateObject();

    cJSON_AddStringToObject(json_data, WIFI_TOTAL_DATA_KEY_MODEL, "");
    cJSON_AddStringToObject(json_data, WIFI_TOTAL_DATA_KEY_MAC, rdata->sta_mac);

    if (strlen(rdata->ap_ssid))
    {
        cJSON_AddStringToObject(json_data, WIFI_TOTAL_DATA_KEY_SSID, rdata->ap_ssid);
    }
    else
    {
        cJSON_AddStringToObject(json_data, WIFI_TOTAL_DATA_KEY_SSID, "");
    }

    cJSON_AddNumberToObject(json_data, WIFI_TOTAL_DATA_KEY_DISTANCE, rdata->distance);

    cJSON_AddItemToArray(json_array, json_data);
}

static __inline cJSON *wifi_data_collect(void)
{

    if (sta_mac_head == NULL || sta_mac_list == NULL)
    {
        return NULL;
    }

    cJSON *data_json = cJSON_CreateObject();
    cJSON *data_array = cJSON_CreateArray();

    //  printf("[%s:%d] sta_mac_head:\n%s\n", __FILE__, __LINE__, cJSON_PrintUnformatted(sta_mac_head));

    int i = 0;
    for (; i < cJSON_GetArraySize(sta_mac_head); ++i)
    {
        //   printf("[%s:%d] i = %d\n", __FILE__, __LINE__, i);

        cJSON *sta_mac_json = cJSON_GetArrayItem(sta_mac_head, i);
        char *sta_mac = sta_mac_json->valuestring;

        cJSON *sta_mac_sub = cJSON_GetObjectItem(sta_mac_list, sta_mac);
        if (sta_mac_sub == NULL)
        {
            goto collect_error;
        }

        wifi_data_t wdata;
        wifi_data_sta_info(&wdata, sta_mac_sub);

        //    printf("[%s:%d] ap_mac_head:\n%s\n", __FILE__, __LINE__, cJSON_PrintUnformatted(ap_mac_head));
        //     printf("[%s:%d] wdata.dst_mac:\n%s\n", __FILE__, __LINE__, wdata.dst_mac);

        report_data_t adata;
        memset(&adata, 0, sizeof(report_data_t));
        if (!json_array_member_exist(wdata.dst_mac, ap_mac_head))
        {
        }
        else
        {
            cJSON *ap_mac_json = cJSON_GetObjectItem(ap_mac_list, wdata.dst_mac);
            wifi_data_ap_info(&adata, ap_mac_json);
        }

        adata.distance = rssi_to_distance(21.0, wdata.rssi);
        memcpy(adata.sta_mac, sta_mac, strlen(sta_mac));
        memcpy(adata.ap_mac, wdata.dst_mac, strlen(wdata.dst_mac));

        report_data_to_json_array(data_array, &adata);
    }

    cJSON_AddNumberToObject(data_json, WIFI_TOTAL_DATA_KEY_TOTAL, i);
    cJSON_AddItemToObject(data_json, WIFI_TOTAL_DATA_KEY_DATA, data_array);

    return data_json;

collect_error:
    cJSON_Delete(data_json);
    cJSON_Delete(data_array);
    return NULL;
}

static __inline void wifi_data_clean(void)
{
    if (sta_mac_head != NULL)
    {
        cJSON_Delete(sta_mac_head);
        sta_mac_head = NULL;
    }

    if (sta_mac_list != NULL)
    {
        cJSON_Delete(sta_mac_list);
        sta_mac_list = NULL;
    }
}

void wifi_data_add_ap(WIFI_AP_INFO_LIST *list)
{
    wifi_data_ap_lock();

    assert(list != NULL);

    if (ap_mac_head == NULL)
    {
        ap_mac_head = cJSON_CreateArray();
    }

    if (ap_mac_list == NULL)
    {
        ap_mac_list = cJSON_CreateObject();
    }

    assert(ap_mac_head != NULL);
    assert(ap_mac_list != NULL);

#if 0

    int j = 0;
    for (; j < list->len; ++j)
    {
        printf("channel = %d\tsignal = %d\tssid = %s\tbssid = %s\n",
               list->ap_info[j].channel,
               list->ap_info[j].siganl,
               list->ap_info[j].ssid,
               list->ap_info[j].bssid);
    }

#endif

    int i = 0;
    for (; i < list->len; ++i)
    {
        wifi_data_add_ap_to_json(&(list->ap_info[i]));
    }

    wifi_data_ap_unlock();
}

void wifi_data_add_sta(wifi_data_list_t *list)
{

    wifi_data_sta_lock();

    assert(list != NULL);

    if (sta_mac_head == NULL)
    {
        sta_mac_head = cJSON_CreateArray();
    }

    if (sta_mac_list == NULL)
    {
        sta_mac_list = cJSON_CreateObject();
    }

    assert(sta_mac_head != NULL);
    assert(sta_mac_list != NULL);
#if 0
    printf("%d\n", list->len);
    int i1 = 0;
    for (; i1 < list->len; ++i1)
    {
        printf("%s\t%s\t%d\n", list->wifi_data_list[i1].src_mac, list->wifi_data_list[i1].dst_mac, list->wifi_data_list[i1].rssi);

        //wifi_data_add_sta_to_json(&(list->wifi_data_list[i]));
    }
    printf("\n");
#endif

    int i = 0;
    for (; i < list->len; ++i)
    {
        // printf("%s\t%s\t%d\n", list->wifi_data_list[i].src_mac, list->wifi_data_list[i].dst_mac, list->wifi_data_list[i].rssi);

        wifi_data_add_sta_to_json(&(list->wifi_data_list[i]));
    }

    wifi_data_sta_unlock();
}

static void report_post_body(char *post_str, int len, char *data_str)
{
    int offset = 0;
    char *ContentHead = "Content-Disposition: form-data; name=";

    snprintf(post_str, len, "--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "device_code");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\r\n", configs_get_device_id());
    offset = strlen(post_str);

    //---------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "device_mac");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\r\n", "00:00:00:00:00:00");
    offset = strlen(post_str);

    //-------------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "device_key");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\r\n", configs_getdevice_key());
    offset = strlen(post_str);

    //----------------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "device_longitude");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%lf\r\n", configs_get_gps_longitude());
    offset = strlen(post_str);

    //--------------------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "device_latitude");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%lf\r\n", configs_get_gps_latitude());
    offset = strlen(post_str);

    //--------------------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "upload_time");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%ld\r\n", 1234567890);
    offset = strlen(post_str);

    //------------------------------------------------------------------------------
    snprintf(post_str + offset, len - offset, "\r\n--%s\r\n", boundary);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\"%s\"\r\n\r\n", ContentHead, "data");
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "%s\r\n", data_str);
    offset = strlen(post_str);

    snprintf(post_str + offset, len - offset, "--%s--\r\n", boundary);
}

report_data_list_t *wifi_data_get_report_data(void)
{
    wifi_data_ap_lock();
    wifi_data_sta_lock();
#if 0
    report_data_list_t *list = NULL;

    int count = cJSON_GetArraySize(sta_mac_head);

    if (count != 0)
    {
        list = (report_data_list_t *)malloc(sizeof(report_data_list_t));
        memset(list, 0, sizeof(report_data_list_t));
        list->len = count;

        list->wifi_data_list = (wifi_data_t *)malloc(sizeof(wifi_data_t) * count);
        memset(list->wifi_data_list, 0, sizeof(wifi_data_t) * count);

        int i = 0;
        for (; i < count; ++i)
        {
            char *key = 
        }
    }


    printf("ap_mac_head %s\n", cJSON_PrintUnformatted(ap_mac_head));
    printf("sta_mac_head %s\n", cJSON_PrintUnformatted(sta_mac_head));
    printf("ap_mac_list %s\n", cJSON_PrintUnformatted(ap_mac_list));
    printf("sta_mac_list %s\n", cJSON_PrintUnformatted(sta_mac_list));
#endif

    cJSON *json_data = wifi_data_collect();

    //printf("[%s:%d]\n", __FILE__, __LINE__);

    if (json_data == NULL)
    {
        wifi_data_sta_unlock();
        wifi_data_ap_unlock();
        return NULL;
    }

    //  printf("%s\n", cJSON_PrintUnformatted(json_data));
    char pstStr[1024 * 100] = {0};

    char url[1024];
    char *host = configs_get_server_host();
    int port = configs_get_server_port();
    char *path = configs_get_server_path();
    snprintf(url, 1024, "http://%s:%d%s", host, port, path);

    printf("url = %s\n", url);

    char buf[102400];
    char *json_data_str = cJSON_PrintUnformatted(json_data);
    report_post_body(pstStr, 102400, json_data_str);

    printf("post body:%s", pstStr);

    http_post(url, pstStr, 3, buf, 102400);

    cJSON_Delete(json_data);
    json_data = NULL;

    wifi_data_clean();

    wifi_data_sta_unlock();
    wifi_data_ap_unlock();

    return NULL;
}